package core

import (
	"fmt"
	"net/http"
	"strconv"
	"strings"

	"github.com/go-kratos/kratos/v2/errors"
	"gorm.io/gorm"
)

var (
	gormErrorsMap = map[error]int32{
		gorm.ErrRecordNotFound:                http.StatusNotFound,            // 记录未找到
		gorm.ErrInvalidTransaction:            http.StatusInternalServerError, // 事务处理错误
		gorm.ErrNotImplemented:                http.StatusInternalServerError, // 功能未实现
		gorm.ErrMissingWhereClause:            http.StatusInternalServerError, // 缺少where条件
		gorm.ErrUnsupportedRelation:           http.StatusInternalServerError, // 关联关系不支持
		gorm.ErrPrimaryKeyRequired:            http.StatusBadRequest,          // 主键未设置
		gorm.ErrModelValueRequired:            http.StatusBadRequest,          // 模型值未设置
		gorm.ErrModelAccessibleFieldsRequired: http.StatusBadRequest,          // 模型字段不可访问
		gorm.ErrSubQueryRequired:              http.StatusBadRequest,          // 子查询未设置
		gorm.ErrInvalidData:                   http.StatusBadRequest,          // 无效的数据
		gorm.ErrUnsupportedDriver:             http.StatusInternalServerError, // 不支持的数据库驱动
		gorm.ErrRegistered:                    http.StatusBadRequest,          // 已注册
		gorm.ErrInvalidField:                  http.StatusBadRequest,          // 无效的字段
		gorm.ErrEmptySlice:                    http.StatusBadRequest,          // 数组不能为空
		gorm.ErrDryRunModeUnsupported:         http.StatusInternalServerError, // 不支持干运行模式
		gorm.ErrInvalidDB:                     http.StatusInternalServerError, // 无效的数据库连接
		gorm.ErrInvalidValue:                  http.StatusBadRequest,          // 无效的数据类型
		gorm.ErrInvalidValueOfLength:          http.StatusBadRequest,          // 关联值无效, 长度不匹配
		gorm.ErrPreloadNotAllowed:             http.StatusBadRequest,          // 使用计数时不允许预加载
		gorm.ErrDuplicatedKey:                 http.StatusUnprocessableEntity, // 唯一性约束冲突
		gorm.ErrForeignKeyViolated:            http.StatusBadRequest,          // 外键约束冲突
		gorm.ErrCheckConstraintViolated:       http.StatusBadRequest,          // 检查约束冲突
	}
)

func NewGormError(err error, reason string, tmpData map[string]string) *errors.Error {
	if err == nil {
		return nil
	}
	if tmpData == nil {
		tmpData = make(map[string]string)
	}
	code, ok := gormErrorsMap[err]
	if !ok || code == 0 {
		code = http.StatusInternalServerError
	}
	rErr := errors.Error{
		Status: errors.Status{
			Code:     code,
			Reason:   reason,
			Message:  err.Error(),
			Metadata: tmpData,
		},
	}
	return rErr.WithCause(err)
}

func GenerateTempDataByID(pk uint32) map[string]string {
	return map[string]string{
		"id": strconv.FormatUint(uint64(pk), 10),
	}
}

const (
	DefaultPage int = 1
	DefaultSize int = 10
)

func PageToOffset(page uint32) uint32 {
	if page < 1 {
		return 0
	}
	return page - 1
}

func CountPages(count int64, size int64) int64 {
	if count == 0 || size == 0 {
		return 0
	}
	return (count + size - 1) / size
}

func StringToUint32(num string) (uint32, error) {
	parsedValue, err := strconv.ParseUint(num, 10, 32)
	if err != nil {
		return 0, fmt.Errorf("string conversion to uint32 failed, key: %v, err: %w", num, err)
	}
	return uint32(parsedValue), nil
}

func StringToListUint32(pks string) []uint32 {
	pks = strings.TrimSpace(pks)
	if pks == "" {
		return make([]uint32, 0)
	}
	pkList := strings.Split(pks, ",")
	var ids []uint32
	for _, pk := range pkList {
		pk = strings.TrimSpace(pk)
		if pk == "" {
			continue
		}
		value, err := strconv.ParseUint(pk, 10, 32)
		if err != nil {
			continue
		}
		ids = append(ids, uint32(value))
	}
	return ids
}
